#include "ge_internal.h"

void perlin_init(int** rp, float** rg1, float** rg2, float** rg3, int* rlen);
float perlin_noise_3D(float vec[3], int terms, float freq, int seed);

static ge_Vertex _ge_cloud_vertices[6] = {
	{ 0.0, 1.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0,-0.5, 0.5, 0.0, { 0.0 } },
	{ 0.0, 0.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0,-0.5,-0.5, 0.0, { 0.0 } },
	{ 1.0, 0.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0, 0.5,-0.5, 0.0, { 0.0 } },
	
	{ 1.0, 1.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0, 0.5, 0.5, 0.0, { 0.0 } },
	{ 0.0, 1.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0,-0.5, 0.5, 0.0, { 0.0 } },
	{ 1.0, 0.0, 0.0, { 1.0, 1.0, 1.0, 1.0 }, 0.0, 0.0, 1.0, 0.5,-0.5, 0.0, { 0.0 } },
};

float perlin_noise_3D_2(float x, float y, float z, int terms, float freq){
	float v[3] = {x,y,z};
	return perlin_noise_3D(v, terms, freq, 42);
}
float cover = 128.0;
float sharp = 0.9;
float freq0 = 1.0;
ge_Image3D* Make3DPerlinTexture(){
	printf("Make3DPerlinTexture 1\n");
	ge_Image3D* img = geCreateSurface3D(64, 64, 64, 0x00000000);
	printf("Make3DPerlinTexture 2\n");
	int t = 7;
	float freq = freq0;
	int x=0, y=0, z=0;
	float offs[3] = { 0.0, 0.0, 0.0 };
	for(z=0; z<img->textureDepth; z++){
		offs[2] += 1.0 / img->textureDepth;
		offs[1] = 0.0;
		for(y=0; y<img->textureHeight; y++){
			offs[1] += 1.0 / img->textureHeight;
			offs[0] = 0.0;
			for(x=0; x<img->textureWidth; x++){
				offs[0] += 1.0 / img->textureWidth;

#define F(x,y,z) ((perlin_noise_3D_2(x, y, z, t, freq)))
				
				float v = (F(offs[0], offs[1], offs[2]) * (1.0 - offs[0]) * (1.0 - offs[1]) * (1.0 - offs[2]) +
						   F(offs[0] - 1.0, offs[1], offs[2]) * (offs[0]) * (1.0 - offs[1]) * (1.0 - offs[2]) +
						   F(offs[0], offs[1] - 1.0, offs[2]) * (1.0 - offs[0]) * (offs[1]) * (1.0 - offs[2]) +
						   F(offs[0], offs[1], offs[2] - 1.0) * (1.0 - offs[0]) * (1.0 - offs[1]) * (offs[2]) +
						   F(offs[0] - 1.0, offs[1] - 1.0, offs[2]) * (offs[0]) * (offs[1]) * (1.0 - offs[2]) +
						   F(offs[0], offs[1] - 1.0, offs[2] - 1.0) * (1.0 - offs[0]) * (offs[1]) * (offs[2]) +
						   F(offs[0] - 1.0, offs[1], offs[2] - 1.0) * (offs[0]) * (1.0 - offs[1]) * (offs[2]) +
						   F(offs[0] - 1.0, offs[1] - 1.0, offs[2] - 1.0) * (offs[0]) * (offs[1]) * (offs[2])
						  ) / (1.0);
				
			//	float v = F(offs[0], offs[1], offs[2]);
			//	v = (v + 1.0) / 2.0;
				u8 c = (u8)( v * 255.0 );
				img->data[x + y*img->textureWidth + z*img->textureHeight*img->textureWidth] = RGBA(c, c, c, 255);
			}
		}
	}
	printf("Make3DPerlinTexture 3\n");
	UpdateGlTexture3D(img);
	printf("Make3DPerlinTexture 4\n");
	return img;
}


ge_Shader* shader_pass1;
ge_Shader* shader_pass2;
ge_Framebuffer* clouds_fbo;
int loc_depth;

int* perlin_p = NULL;
float* perlin_g3 = NULL;
int perlin_len = 0;

ge_Renderer* clouds_init(){
	clouds_fbo = geCreateFramebuffer(geGetContext()->width, geGetContext()->height);
	geTextureWrap(clouds_fbo->texture, GE_CLAMP_TO_EDGE, GE_CLAMP_TO_EDGE);
	shader_pass1 = geCreateShader();
	geShaderLoadVertexSource(shader_pass1, "scene/shaders/cloudv2.vert");
	geShaderLoadFragmentSource(shader_pass1, "scene/shaders/cloudv2_pass1.frag");
	shader_pass2 = geCreateShader();
	geShaderLoadVertexSource(shader_pass2, "scene/shaders/cloudv2.vert");
	geShaderLoadFragmentSource(shader_pass2, "scene/shaders/cloudv2.frag");
	geShaderUse(shader_pass2);

	loc_depth = geShaderUniformID(shader_pass2, "ge_Texture1");
	geShaderUniform1i(geShaderUniformID(shader_pass2, "ge_Texture2"), 2);

	ge_Renderer* render = geCreateRenderer(shader_pass2);
	render->blend_enabled = true;
	ge_Object* obj = geLoadObject("data/sphere2.obj");
	
	obj->verts = (ge_Vertex*)geRealloc(obj->verts, sizeof(ge_Vertex) * obj->nVerts * 2);
	memcpy(&obj->verts[obj->nVerts], obj->verts, sizeof(ge_Vertex) * obj->nVerts);
	obj->nVerts *= 2;
	int i;
	for(i=0; i<obj->nVerts; i++){
		obj->verts[i].x = obj->verts[i].x * 0.3;
		obj->verts[i].y = obj->verts[i].y * 0.3;
		obj->verts[i].z = obj->verts[i].z * 0.3;
	}
	for(i=obj->nVerts/2; i<obj->nVerts; i++){
		obj->verts[i].x += 0.5;
	}

	geRendererLinkObject(render, obj);
	geRendererCreateContext(NULL, render);
	
	ge_Image3D* perlin_tex = Make3DPerlinTexture();
	obj->material.textures[2] = (ge_Image*)perlin_tex;
	
	perlin_init(&perlin_p, NULL, NULL, &perlin_g3, &perlin_len);
	geShaderUse(shader_pass2);
	glUniform1iv(geShaderUniformID(shader_pass2, "perlin_p"), perlin_len, perlin_p);
	glUniform3fv(geShaderUniformID(shader_pass2, "perlin_g3"), perlin_len, perlin_g3);
	
	return render;
}

void clouds(ge_Renderer* render){
	geObjectMatrixIdentity(render->objs[0]);
	geObjectMatrixScale(render->objs[0], 3.0, 3.0, 3.0);
	geRendererUse(render);
	
	render->shader = shader_pass1;
	geFramebufferUse(clouds_fbo);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glFrontFace(GL_CW);
	geRenderObjects(render);
	glFrontFace(GL_CCW);
	geFramebufferUse(NULL);
	
	render->shader = shader_pass2;
	render->objs[0]->material.textures[0] = clouds_fbo->texture;
	geRenderObjects(render);
}

typedef struct Cloud {
	float x, y, z;
	s32 ready;
} Cloud;

typedef struct CloudsGenerator {
	ge_Renderer* render;
	ge_Cloud* clouds;
	float positions[256 * 3];
	int nPositions;
	int loc_position;
	int loc_lighting;
} CloudsGenerator;

CloudsGenerator* clouds2_init(int nClouds, int nParts){
	int i, j, k;
	srand(time(NULL));

	float terrain = 20000.0;
	float size = 1000.0;
	float altitude = 3000.0;
//	size = 2.0;

	CloudsGenerator* generator = (CloudsGenerator*)geMalloc(sizeof(CloudsGenerator));
	generator->nPositions = min(128, nClouds);

	ge_Shader* shader = geCreateShader();
	geShaderLoadVertexSource(shader, "scene/shaders/billboards.vert");
	geShaderLoadFragmentSource(shader, "scene/shaders/billboards.frag");
	generator->loc_position = geShaderUniformID(shader, "position");
	generator->loc_lighting = geShaderUniformID(shader, "lighting");

	generator->render = geCreateRenderer(shader);

	ge_Object* obj = (ge_Object*)geMalloc(sizeof(ge_Object));
	obj->material.textures[0] = geLoadImage("data/cloud.bmp");
	geTextureWrap(obj->material.textures[0], GE_CLAMP_TO_EDGE, GE_CLAMP_TO_EDGE);
	geTextureMode(obj->material.textures[0], GE_NEAREST);
	obj->verts = (ge_Vertex*)geMalloc(sizeof(ge_Vertex) * nClouds * nParts * 6);
	obj->nVerts = nClouds * nParts * 6;
	for(i=0; i<nClouds; i++){
		float a = rand() % 360;
		float r = geCos(rand()) * terrain;
		float r2 = geSin(rand()) * terrain;
		
		generator->positions[i*3+0] = r * geCos(a);
		generator->positions[i*3+1] = r2 * geSin(a);
		generator->positions[i*3+2] = altitude;
		
		for(j=0; j<nParts; j++){
			float a = rand() % 360;
			float x = ((rand() % (int)size*3) - size) * geCos(a);
			float y = ((rand() % (int)size*3) - size) * geSin(a);
			float z = 0.1 * ((rand() % (int)(size/32.0)) - (size/64.0)) * geSin(rand() % 360);
			for(k=0; k<6; k++){
				obj->verts[(i*nParts+j)*6 + k].w = (float)i;
				obj->verts[(i*nParts+j)*6 + k].u = _ge_cloud_vertices[k].u / 2.0;
				obj->verts[(i*nParts+j)*6 + k].v = _ge_cloud_vertices[k].v / 2.0;
				obj->verts[(i*nParts+j)*6 + k].color[0] = _ge_cloud_vertices[k].x;
				obj->verts[(i*nParts+j)*6 + k].color[1] = _ge_cloud_vertices[k].y;
				obj->verts[(i*nParts+j)*6 + k].color[2] = _ge_cloud_vertices[k].z;
				obj->verts[(i*nParts+j)*6 + k].color[3] = 1.0;
				obj->verts[(i*nParts+j)*6 + k].x = x;
				obj->verts[(i*nParts+j)*6 + k].y = y;
				obj->verts[(i*nParts+j)*6 + k].z = z;
			}
		}
	}
	//generator->render->depth_enabled = false;
	generator->render->depth_mask = false;
	generator->render->blend_enabled = true;
	geRendererLinkObject(generator->render, obj);
	geRendererCreateContext(NULL, generator->render);

	return generator;
}

void clouds2(CloudsGenerator* generator, float lighting){
	int i;
	for(i=0; i<generator->nPositions; i++){
		generator->positions[i*3+0] += 1.0;
	}
	glDepthFunc(GL_LEQUAL);
	geShaderUse(generator->render->shader);
	glUniform3fv(generator->loc_position, generator->nPositions, generator->positions);
	glUniform1f(generator->loc_lighting, lighting);
	geRenderObjects(generator->render);
}

void _ge_CreateBox(const char* path, const char* out);
void glow(){
	geInit();
	geSplashscreenEnable(false);
	geCreateMainWindow("GE-Glow", 1280, 720, 0);
//	geCreateMainWindow("GE-Glow", -1, -1, GE_WINDOW_FULLSCREEN);
	geDebugMode(GE_DEBUG_ALL);
	geCursorRoundMode(true);
	geWaitVsync(true);
//	_ge_CreateBox("data/msg_box", "data/msg_box/black.bin");

	geClearColor(0xFFFF8080);
	ge_Font* font = geLoadFont("");
	geFontSize(font, 20);

	CloudsGenerator* gen = clouds2_init(100, 16);
//	ge_Renderer* render = clouds_init();

	ge_Shader* shader_pass1 = geCreateShader();
	geShaderLoadVertexSource(shader_pass1, "default_shaders/generic_glow.vert");
	geShaderLoadFragmentSource(shader_pass1, "default_shaders/generic_glow1.frag");
	ge_Shader* shader_pass2 = geCreateShader();
	geShaderLoadVertexSource(shader_pass2, "default_shaders/generic_glow.vert");
	geShaderLoadFragmentSource(shader_pass2, "default_shaders/generic_glow2.frag");
	
	ge_Keys* keys = geCreateKeys();
	ge_Camera* cam = geCreateCamera();
	geCameraSetMaximumAngles(cam, -89.0, 89.0, GE_CAMERA_VERTICAL);
	cam->z = 1.80;
	ge_Scene* scene = geLoadScene("scene/scene.ge3d");

	ge_Framebuffer* fbo = geCreateFramebuffer(geGetContext()->width, geGetContext()->height);
	ge_Framebuffer* fbo2 = geCreateFramebuffer(geGetContext()->width, geGetContext()->height);
	geTextureWrap(fbo->texture, GE_CLAMP_TO_EDGE, GE_CLAMP_TO_EDGE);
	geTextureWrap(fbo2->texture, GE_CLAMP_TO_EDGE, GE_CLAMP_TO_EDGE);
	geTextureWrap(fbo->depth, GE_CLAMP_TO_EDGE, GE_CLAMP_TO_EDGE);

	geShaderUse(shader_pass1);
	geTextureImage(1, fbo->depth);
	geTextureImage(0, NULL);
	geShaderUniform1i(geShaderUniformID(shader_pass1, "ge_Texture1"), 1);
	geShaderUse(NULL);
	
	ge_LensFlare* lensFlare = geGfxLensFlareCreate(NULL, NULL, NULL, fbo->depth);
	lensFlare->alpha = 1.5;
	ge_LightScatter* lightScatter = geLightScatterCreate(4, NULL, fbo->texture, fbo->depth);
	float matrix[16];
	float sun_pos[4] = { 0.0, 0.0, 0.0, 1.0 };
	float sun_pos_2d[4] = { 0.0 };
	float* p_sun_pos_2d[1] = { sun_pos_2d };

	bool focused = true;

	while(1){
		geReadKeys(keys);
		float speed = 20.0;
		if(keys->pressed[GEK_ESCAPE]){
			focused = false;
			geCursorRoundMode(false);
		}
		if(!focused){
			if(keys->pressed[GEK_LBUTTON]){
				focused = true;
				geCursorRoundMode(true);
			}
		}
		if(focused){
			if(keys->pressed[GEK_CONTROL]){
				speed = 0.01;
			}
			if(keys->pressed[GEK_SHIFT]){
				cam->z -= 1.0 / speed;
			}
			if(keys->pressed[GEK_SPACE]){
				cam->z += 1.0 / speed;
			}
			if(keys->pressed['Z']){
				geCameraMoveForward(cam, speed);
			}
			if(keys->pressed['S']){
				geCameraMoveBack(cam, speed);
			}
			if(keys->pressed['Q']){
				geCameraMoveLeft(cam, speed);
			}
			if(keys->pressed['D']){
				geCameraMoveRight(cam, speed);
			}
			if(geKeysToggled(keys, 'P')){
				geFreeShader(gen->render->shader);
				gen->render->shader = geCreateShader();
				geShaderLoadVertexSource(gen->render->shader, "scene/shaders/billboards.vert");
				geShaderLoadFragmentSource(gen->render->shader, "scene/shaders/billboards.frag");
			}
			geCameraRotateWithMouse(cam, 0.1);
		}

		geDrawingMode(GE_DRAWING_MODE_3D);
		geCameraLook(cam);
	//	geFramebufferUse(fbo);
		geClearScreen();
		geSceneDraw(scene);
		clouds2(gen, 1.0);
	//	clouds(render);
		geFramebufferUse(NULL);
		
		geMatrix44Mult(matrix, geGetMatrix(GE_MATRIX_PROJECTION), geGetMatrix(GE_MATRIX_VIEW));
	//	ge_Translate(matrix, scene->lights[8].position.x, scene->lights[8].position.y, scene->lights[8].position.z);
		geMatrix44Vec4Mult(sun_pos_2d, matrix, sun_pos);
		sun_pos_2d[0] /= sun_pos_2d[3];
		sun_pos_2d[1] /= sun_pos_2d[3];
		sun_pos_2d[2] /= sun_pos_2d[3];

		geDrawingMode(GE_DRAWING_MODE_2D);
		/*
		geFramebufferUse(fbo2);
		geShaderUse(shader_pass1);
		geTextureImage(1, fbo->depth);
		geTextureImage(0, NULL);
		geBlitImage(0, 0, fbo->texture, 0, 0, fbo->texture->width, fbo->texture->height, GE_BLIT_VFLIP | GE_BLIT_NOALPHA);
		geFramebufferUse(NULL);
		geShaderUse(NULL);
		
		geDrawingMode(GE_DRAWING_MODE_2D | GE_DRAWING_2D_DEPTH);
		geClearMode(GE_CLEAR_DEPTH_BUFFER | GE_CLEAR_COLOR_BUFFER);
		geClearScreen();
		geLightScatterRender(lightScatter, p_sun_pos_2d, 1);
		
		geDrawingMode(GE_DRAWING_MODE_2D);
		geShaderUse(shader_pass2);
		geBlendFunc(GE_ONE, GE_ONE);
		geBlitImage(0, 0, fbo2->texture, 0, 0, fbo2->texture->width, fbo2->texture->height, GE_BLIT_VFLIP);
		geBlendFunc(GE_DEFAULT, GE_DEFAULT);
		geShaderUse(NULL);
		
		geGfxLensFlareRender(lensFlare, sun_pos_2d);
		*/
		
		geFontPrintfScreen(0, 0, font, 0xFFFFFFFF, "FPS: %d", geFps());
		geSwapBuffers();
	}
}
